﻿using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using WeShare.WeiXin.Common.EnumKey;
#if !NET40
using System.Threading.Tasks;
#endif
namespace WeShare.WeiXin.Common
{
    public class HttpUtil
    {

        private static HttpWebRequest CreateRequest(string url, HttpMethod method, string postData = "", string certpath = "", string certpwd = "")
        {
            var request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = method.ToString();
            request.ContentType = "application/x-www-form-urlencoded";
            request.Accept = "*/*";
            request.Timeout = 15000;
            request.AllowAutoRedirect = false;
            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback((a, b, c, d) => true);
            if (!string.IsNullOrEmpty(certpath) && !string.IsNullOrEmpty(certpwd))
            {
                X509Certificate2 cer = new X509Certificate2(certpath, certpwd,
                    X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet);
                request.ClientCertificates.Add(cer);
            }
            if (method == HttpMethod.POST)
            {
                using (var sw = new StreamWriter(request.GetRequestStream()))
                {
                    sw.Write(postData);
                }
            }
            return request;
        }

        public static string HttpGet(string url)
        {
            var request = CreateRequest(url, HttpMethod.GET);
            return GetResponseString(request);
        }
        public static string HttpPost(string url, string postData, string certPath = "", string certPwd = "")
        {
            var request = CreateRequest(url, HttpMethod.POST, postData, certPath, certPwd);
            return GetResponseString(request);
        }
        private static string GetResponseString(HttpWebRequest request)
        {
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                {
                    return reader.ReadToEnd();//获取响应
                }
            }
        }
        public static string HttpGet(string url, FileStreamInfo stream)
        {
            var request = CreateRequest(url, HttpMethod.GET);
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                var respStream = response.GetResponseStream();
                var disposition = response.Headers["Content-disposition"];
                if (!string.IsNullOrEmpty(disposition))
                {
                    stream.FileName = disposition.Split('=')[1].Trim('"');
                    respStream?.CopyTo(stream);
                    return "";
                }
                else
                {
                    using (StreamReader reader = new StreamReader(respStream, Encoding.UTF8))
                    {
                        return reader.ReadToEnd();//获取响应
                    }
                }
            }
        }
        public static string HttpPost(string url, string postData, FileStreamInfo stream)
        {
            var request = CreateRequest(url, HttpMethod.POST, postData);
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                var respStream = response.GetResponseStream();
                var disposition = response.Headers["Content-disposition"];
                if (!string.IsNullOrEmpty(disposition))
                {
                    stream.FileName = disposition.Split('=')[1].Trim('"');
                    respStream?.CopyTo(stream);
                    return "";
                }
                else if (response.ContentType.Contains("text") == false &&
                         response.ContentType.Contains("json") == false &&
                         response.ContentType.Contains("xml") == false)
                {
                    respStream?.CopyTo(stream);
                    return "";
                }
                else
                {
                    using (StreamReader reader = new StreamReader(respStream, Encoding.UTF8))
                    {
                        return reader.ReadToEnd();//获取响应
                    }
                }
            }
        }
        /// <summary>
        /// 发起POST请求，并获取请求返回值
        /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="obj">数据实体</param>
        /// <param name="url">接口地址</param>
        public static T PostResult<T>(object obj, string url)
        {
            //序列化设置
            var setting = new JsonSerializerSettings();
            //解决枚举类型序列化时，被转换成数字的问题
            setting.Converters.Add(new StringEnumConverter());
            setting.NullValueHandling = NullValueHandling.Ignore;
            var retdata = HttpPost(url, JsonConvert.SerializeObject(obj, setting));
            return JsonConvert.DeserializeObject<T>(retdata);
        }

        /// <summary>
        /// 发起GET请求，并获取请求返回值
        /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="url">接口地址</param>
        public static T GetResult<T>(string url)
        {
            var retdata = HttpGet(url);
            return JsonConvert.DeserializeObject<T>(retdata);
        }
        public static T GetResult<T>(string url, FileStreamInfo stream)
        {
            var retdata = HttpGet(url, stream);
            if (retdata == "")
            {
                return default(T);
            }
            return JsonConvert.DeserializeObject<T>(retdata);
        }
        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="url"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static string UpLoadFile(string url, string fileName)
        {
            using (var client = new WebClient())
            {
                var b = client.UploadFile(url, "POST", fileName);
                return System.Text.Encoding.UTF8.GetString(b);
            }
        }
        public static string UpLoadFile(string url, List<FormEntity> form)
        {
            using (var client = new WebClient())
            {
                //分割字符串
                var boundary = "----" + DateTime.Now.Ticks.ToString("x");
                client.Headers.Add("Content-Type", "multipart/form-data; boundary=" + boundary);
                using (MemoryStream stream = new MemoryStream())
                {
                    #region 将非文件表单写入内存流
                    foreach (var entity in form.Where(f => f.IsFile == false))
                    {
                        var temp = new StringBuilder();
                        temp.AppendFormat("\r\n--{0}", boundary);
                        temp.Append("\r\nContent-Disposition: form-data;");
                        temp.AppendFormat("name=\"{0}\"", entity.Name);
                        temp.Append("\r\n\r\n");
                        temp.Append(entity.Value);
                        byte[] tempb = Encoding.UTF8.GetBytes(temp.ToString());
                        stream.Write(tempb, 0, tempb.Length);
                    }
                    #endregion
                    #region 将文件表单写入内存流
                    foreach (var entity in form.Where(f => f.IsFile == true))
                    {
                        byte[] filedata = null;
                        var filename = Path.GetFileName(entity.Value);
                        //表示是网络资源
                        if (entity.Value.Contains("http"))
                        {
                            //处理网络文件
                            using (var c = new WebClient())
                            {
                                filedata = c.DownloadData(entity.Value);
                            }
                        }
                        else
                        {
                            //处理物理路径文件
                            using (FileStream file = new FileStream(entity.Value,
                            FileMode.Open, FileAccess.Read))
                            {
                                filedata = new byte[file.Length];
                                file.Read(filedata, 0, (int)file.Length);
                            }
                        }
                        var temp = string.Format("\r\n--{0}\r\nContent-Disposition: " +
                        "form-data; name=\"{1}\"; filename=\"{2}\"\r\n\r\n",
                           boundary, entity.Name, filename);
                        byte[] tempb = Encoding.UTF8.GetBytes(temp);
                        stream.Write(tempb, 0, tempb.Length);
                        stream.Write(filedata, 0, filedata.Length);
                    }
                    #endregion
                    //结束标记
                    byte[] foot_data = Encoding.UTF8.GetBytes("\r\n--" + boundary +
                    "--\r\n");
                    stream.Write(foot_data, 0, foot_data.Length);
                    var b = client.UploadData(url, "POST", stream.GetBuffer());
                    return System.Text.Encoding.UTF8.GetString(b);
                }

            }
        }
        public static T UploadResult<T>(string url, string fileName)
        {
            var retdata = UpLoadFile(url, fileName);
            return JsonConvert.DeserializeObject<T>(retdata);
        }

        public static T UploadResult<T>(string url, List<FormEntity> form)
        {
            var retdata = UpLoadFile(url, form);
            return JsonConvert.DeserializeObject<T>(retdata);
        }

#if !NET40
        public static async Task<string> UpLoadFileAsync(string url, string fileName)
        {
            using (var client = new WebClient())
            {
                var b = await client.UploadFileTaskAsync(new Uri(url), "POST", fileName);
                return System.Text.Encoding.UTF8.GetString(b);
            }
        }

        public static async Task<string> UpLoadFileAsync(string url, List<FormEntity> form)
        {
            using (var client = new WebClient())
            {
                //分割字符串
                var boundary = "----" + DateTime.Now.Ticks.ToString("x");
                client.Headers.Add("Content-Type", "multipart/form-data; boundary=" + boundary);
                using (MemoryStream stream = new MemoryStream())
                {
                    #region 将非文件表单写入内存流
                    foreach (var entity in form.Where(f => f.IsFile == false))
                    {
                        var temp = new StringBuilder();
                        temp.AppendFormat("\r\n--{0}", boundary);
                        temp.Append("\r\nContent-Disposition: form-data;");
                        temp.AppendFormat("name=\"{0}\"", entity.Name);
                        temp.Append("\r\n\r\n");
                        temp.Append(entity.Value);
                        byte[] tempb = Encoding.UTF8.GetBytes(temp.ToString());
                        await stream.WriteAsync(tempb, 0, tempb.Length);
                    }
                    #endregion
                    #region 将文件表单写入内存流
                    foreach (var entity in form.Where(f => f.IsFile == true))
                    {
                        byte[] filedata = null;
                        var filename = Path.GetFileName(entity.Value);
                        //表示是网络资源
                        if (entity.Value.Contains("http"))
                        {
                            //处理网络文件
                            using (var c = new WebClient())
                            {
                                filedata = await c.DownloadDataTaskAsync(entity.Value);
                            }
                        }
                        else
                        {
                            //处理物理路径文件
                            using (FileStream file = new FileStream(entity.Value,
                            FileMode.Open, FileAccess.Read))
                            {
                                filedata = new byte[file.Length];
                                await file.ReadAsync(filedata, 0, (int)file.Length);
                            }
                        }
                        var temp = string.Format("\r\n--{0}\r\nContent-Disposition: " +
                        "form-data; name=\"{1}\"; filename=\"{2}\"\r\n\r\n",
                           boundary, entity.Name, filename);
                        byte[] tempb = Encoding.UTF8.GetBytes(temp);
                        await stream.WriteAsync(tempb, 0, tempb.Length);
                        await stream.WriteAsync(filedata, 0, filedata.Length);
                    }
                    #endregion
                    //结束标记
                    byte[] foot_data = Encoding.UTF8.GetBytes("\r\n--" + boundary +
                    "--\r\n");
                    await stream.WriteAsync(foot_data, 0, foot_data.Length);
                    var b = await client.UploadDataTaskAsync(url, "POST", stream.GetBuffer());
                    return System.Text.Encoding.UTF8.GetString(b);
                }

            }
        }

        public static async Task<T> UploadResultAsync<T>(string url, List<FormEntity> form)
        {
            var retdata = await UpLoadFileAsync(url, form);
            return JsonConvert.DeserializeObject<T>(retdata);
        }
        public static async Task<string> HttpGetAsync(string url)
        {
            var request = CreateRequest(url, HttpMethod.GET);
            return await GetResponseStringAsync(request);
        }
        public static async Task<string> HttpGetAsync(string url, FileStreamInfo stream)
        {
            var request = CreateRequest(url, HttpMethod.GET);
            using (HttpWebResponse response = await request.GetResponseAsync() as HttpWebResponse)
            {
                if (response != null)
                {
                    var respStream = response.GetResponseStream();
                    var disposition = response.Headers["Content-disposition"];
                    if (!string.IsNullOrEmpty(disposition))
                    {
                        stream.FileName = disposition.Split('=')[1].Trim('"');
                        if (respStream != null) await respStream?.CopyToAsync(stream);
                        return "";
                    }
                    else
                    {
                        using (StreamReader reader = new StreamReader(respStream, Encoding.UTF8))
                        {
                            return await reader.ReadToEndAsync();//获取响应
                        }
                    }
                }

                return "";
            }
        }
        public static async Task<string> HttpPostAsync(string url, string postData, string certPath = "", string certPwd = "")
        {
            var request = CreateRequest(url, HttpMethod.POST, postData, certPath, certPwd);
            return await GetResponseStringAsync(request);
        }
        public static async Task<string> HttpPostAsync(string url, string postData, FileStreamInfo stream)
        {
            var request = CreateRequest(url, HttpMethod.POST, postData);
            if (request != null)
                using (HttpWebResponse response = await request.GetResponseAsync() as HttpWebResponse)
                {
                    var respStream = response.GetResponseStream();
                    var disposition = response.Headers["Content-disposition"];
                    if (!string.IsNullOrEmpty(disposition))
                    {
                        stream.FileName = disposition.Split('=')[1].Trim('"');
                        respStream?.CopyToAsync(stream);
                        return "";
                    }
                    else if (response.ContentType.Contains("text") == false &&
                             response.ContentType.Contains("json") == false &&
                             response.ContentType.Contains("xml") == false)
                    {
                        respStream?.CopyToAsync(stream);
                        return "";
                    }
                    else
                    {
                        using (StreamReader reader = new StreamReader(respStream, Encoding.UTF8))
                        {
                            return await reader.ReadToEndAsync(); //获取响应
                        }
                    }
                }

            return "";
        }

        private static async Task<string> GetResponseStringAsync(HttpWebRequest request)
        {
            using (var response = await request.GetResponseAsync() as HttpWebResponse)
            {
                using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                {
                    return reader.ReadToEnd();//获取响应
                }
            }
        }

        /// <summary>
        /// 发起POST请求，并获取请求返回值
        /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="obj">数据实体</param>
        /// <param name="url">接口地址</param>
        public static async Task<T> PostResultAsync<T>(object obj, string url)
        {
            //序列化设置
            var setting = new JsonSerializerSettings();
            //解决枚举类型序列化时，被转换成数字的问题
            setting.Converters.Add(new StringEnumConverter());
            setting.NullValueHandling = NullValueHandling.Ignore;
            var retdata = await HttpPostAsync(url, JsonConvert.SerializeObject(obj, setting));
            return JsonConvert.DeserializeObject<T>(retdata);
        }

        /// <summary>
        /// 发起GET请求，并获取请求返回值
        /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="url">接口地址</param>
        public static async Task<T> GetResultAsync<T>(string url)
        {
            var retdata = await HttpGetAsync(url);
            return JsonConvert.DeserializeObject<T>(retdata);
        }

#endif

    }
}
